---
title: Getting Started
---

import { Callout } from 'nextra/components';

import DocBasic from '../../components/doc-basic';
import DocMotion from '../../components/doc-motion';
import DocCover from '../../components/doc-cover';
import DocToolbar from '../../components/doc-toolbar';
import DocRead from '../../components/doc-read';
import DocButton from '../../components/doc-button';
import DocLoop from '../../components/doc-loop';
import DocBackdrop from '../../components/doc-backdrop';
import DocPure from '../../components/doc-pure';
import DocClose from '../../components/doc-close';
import DocBroken from '../../components/doc-broken';
import DocControlled from '../../components/doc-controlled';
import DocPart from '../../components/doc-part';
import DocOverlay from '../../components/doc-overlay';
import DocRender from '../../components/doc-render';

## 安装

在 `React` 项目目录运行以下命令：

pnpm:

```plaintext
pnpm i react-photo-view
```

yarn:

```plaintext
yarn add react-photo-view
```

或者用 npm:

```plaintext
npm install react-photo-view
```

## 快速开始

在应用入口处引入 `CSS` 文件

```js
import 'react-photo-view/dist/react-photo-view.css';
```

在需要使用的地方引入 `PhotoProvider` 和 `PhotoView` 组件，基本用法：

```jsx
import { PhotoProvider, PhotoView } from 'react-photo-view';

export default function MyComponent() {
  return (
    <PhotoProvider>
      <div className="foo">
        {images.map((item, index) => (
          <PhotoView key={index} src={item}>
            <img src={item} alt="" />
          </PhotoView>
        ))}
      </div>
    </PhotoProvider>
  );
}
```

以 `PhotoProvider`为界限，里面所有的 `PhotoView` 图片会按照运行顺序提取为一组图片预览画廊。当某个 `<img />` 被点击，则会定位到指定的图片并打开预览：

<DocBasic />

如果需要整个页面的图片形成一组进行预览，则可以在页面的入口处嵌套 `PhotoProvider`。

<Callout emoji="💡">
  注意： `react-photo-view` 一次只在 `DOM` 中加载保留三张图片，因为每张图片都是一个合成层，这会消耗相当多的内存。
</Callout>

## 过渡动画

### 过渡效果

`react-photo-view` 为保证更流畅体验，使用了相当多的 `CSS3` 动画，在滚动时使用弹簧动画贴近原生滚动效果。`CSS3` 动画默认持续时间为 `400ms`，使用 `cubic-bezier(0.25, 0.8, 0.25, 1)` 动画函数。

可通过设置 `speed` 、`easing` 来自定义动画时间或函数：

```jsx
<PhotoProvider
  speed={() => 800}
  easing={(type) => (type === 2 ? 'cubic-bezier(0.36, 0, 0.66, -0.56)' : 'cubic-bezier(0.34, 1.56, 0.64, 1)')}
/>
```

`speed` 、`easing`为函数，可通过参数 `type` 判断当前动画的类型，`type` 的值有：

- `1` - 打开中
- `2` - 关闭中
- `3` - 切换 `index`

<DocMotion />

<Callout emoji="💡">若子节点不是 `img` 节点将额外添加 `opacity` 淡入淡出效果。</Callout>

### 裁切缩略图

`<PhotoView />` 组件会自动识别子节点类型，如果子节点为 `<img />`，并且使用 `CSS` 设置了 `object-fit` 属性，则在缩放动画中附带裁切效果：

```jsx
<PhotoView src={imageURL}>
  <img src={imageURL} style={{ objectFit: 'cover' }} alt="" />
</PhotoView>
```

<DocCover />

## 自定义工具栏

在 `<PhotoProvider />` 上添加参数 `toolbarRender` 函数，该函数须返回一个 `ReactNode` 节点。

### 放大/缩小

添加对 `scale` 的控制即可实现放大/缩小按钮：

```jsx
<PhotoProvider
  toolbarRender={({ onScale, scale }) => {
    return (
      <>
        <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onScale(scale + 1)} />
        <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onScale(scale - 1)} />
      </>
    );
  }}
/>
```

### 旋转

`react-photo-view` 默认不内置旋转控制按钮，但提供了旋转 `API`。我们只需简单添加一个 `SVG` 图片即可实现旋转效果：

```jsx
<PhotoProvider
  toolbarRender={({ rotate, onRotate }) => {
    return <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onRotate(rotate + 90)} />;
  }}
/>
```

`toolbarRender` 函数提供的参数有：

- `images` - 图片列表 `DataType[]`
- `index` - 当前索引 `number`
- `onIndexChange` - 索引改变回调 `(index: number) => void`
- `visible` - 是否可见 `boolean`
- `onClose` - 关闭事件回调 `() => void`
- `overlayVisible` - 覆盖物是否可见 `boolean`
- `overlay` - 图片覆盖物
- `rotate` - 当前旋转角度 `number`
- `onRotate` - 旋转事件回调 `(rotate: number) => void`
- `scale` - 当前缩放 `number`
- `onScale` - 缩放事件回调 `(scale: number) => void`

以下示例实现了**旋转/全屏/放大/缩小**工具栏：

<DocToolbar />

## 长图模式

移动设备加载长宽比例超过 `3` 倍的图片，默认会进入长图模式。即开始预览在图片顶部，图片宽度填充移动设备宽度：

<DocRead />

## 触发节点

`<PhotoView>` 子组件可以是常规 `React.HTMLAttributes` 类型，如 `<img />`、`<div />` 等。

如果是自定义组件则需要保证 `onClick` 事件能正常触发，转发 `ref` 到 `HTMLElement` 上保证打开/关闭动画源的正确性。

```jsx
<PhotoView src={imageURL}>
  <Button primary>Click</Button>
</PhotoView>
```

<DocButton />

## `PhotoSlider`

一般情况，使用 `<PhotoProvider />` 配合 `<PhotoView />` 已经满足大部分需求。如果有更高级的自定义控制，可以使用 `<PhotoSlider />`。

`<PhotoSlider />` 的参数继承自 `<PhotoProvider />`。会额外暴露几个 API：

- `images` 图片列表，`DataType[]`
- `visible` 是否可见受控属性 `boolean`
- `onClose` 关闭事件回调 `() => void`
- `afterClose` 关闭动画结束后回调 `() => void`
- `index` 当前索引受控属性 `number`
- `onIndexChange` 索引改变回调 `(index: number) => void`

`visible` 和 `onClose` 搭配形成受控，`index` 搭配 `onIndexChange` 形成受控。

```jsx
export default function MyComponent() {
  const [visible, setVisible] = useState(false);
  const [index, setIndex] = useState(0);

  return (
    <>
      <Button onClick={() => setIndex(2)}>setIndex(2)</Button>
      <Button onClick={() => setIndex(4)}>setIndex(4)</Button>
      <Button onClick={() => setVisible(true)} primary>
        Click
      </Button>

      <PhotoSlider
        images={images.map((item) => ({ src: item, key: item }))}
        visible={visible}
        onClose={() => setVisible(false)}
        index={index}
        onIndexChange={setIndex}
      />
    </>
  );
}
```

<DocControlled />

## 预览更多

`<PhotoView />` 无子组件的情况下也能加入到预览队列，只是无法直接触发当前图片的预览。通过这个特性，我们可以实现经过一个入口，预览更多图片的功能：

```jsx
<PhotoProvider>
  {images.map((item, index) => (
    <PhotoView key={index} src={item}>
      {index < 2 ? <img src={item} alt="" /> : undefined}
    </PhotoView>
  ))}
</PhotoProvider>
```

<DocPart />

## 自定义渲染节点

`<PhotoView />` 默认内置支持图片的预览，只需传递 `src` 即可，组件会自动识别图片的宽高。

我们也可以通过 `render` 函数实现自定义节点的预览，以支持 `video` 或 `pdf` 的需求。

```jsx
const elementSize = 400;

function MyComponent() {
  return (
    <PhotoView
      width={elementSize}
      height={elementSize}
      render={({ scale, attrs }) => {
        const width = parseFloat((attrs?.style?.width ?? 0) as string);
        const offset = (width - elementSize) / elementSize;
        const childScale = scale === 1 ? scale + offset : 1 + offset;

        return (
          <div {...attrs}>
            <div style={{ transform: `scale(${childScale})`, width: elementSize, transformOrigin: '0 0' }}>
              <div>Hello world</div>
              <Button>button</Button>
              <input onMouseDown={(e) => e.stopPropagation()} />
            </div>
          </div>
        );
      }}
    >
      <Button primary>Click</Button>
    </PhotoView>
  );
}
```

<Callout emoji="💡">
  注意：画廊在缩放之后，先会用 `scale` 进行缩放，在动画结束后则调整为当前 `scale` 所对应的宽高，并重置 `scale` 为 `1`。
</Callout>

<DocRender />

## PhotoProvider 参数

### 自定义描述

在 `<PhotoProvider />` 上添加 `overlayRender` 函数，可以实现自定义节点，**函数参数返回值与 `toolbarRender` 一致**。

<DocOverlay />

### 循环预览

在 `<PhotoProvider />` 上添加参数 `loop` 可以更改循环预览数量。设为 `boolean` 启用/关闭，设为 `number` 类型则超过具体数量以开启循环预览，默认值为 `3` 张：

```jsx
<PhotoProvider loop={4} />
```

<DocLoop />

### 蒙层透明度

预设为不透明，上拉过程中会慢慢减少透明度，可在 `<PhotoProvider />` 添加 `maskOpacity` 调整默认透明度，可设置为 `0-1` 之间的数字：

```jsx
<PhotoProvider maskOpacity={0.5} />
```

<DocBackdrop />

### 不显示顶部按钮区域

设置 `bannerVisible` 为 `false` 则可以不显示顶部区域：

```jsx
<PhotoProvider bannerVisible={false} />
```

<DocPure />

### 蒙层事件

`pullClosable`(默认 `true`) 是否可以通过下拉关闭画廊，`maskClosable`(默认 `true`) 是否可以点击蒙版关闭画廊：

```jsx
<PhotoProvider pullClosable={false} maskClosable={false} />
```

<DocClose />

## 自定义反馈

`<PhotoView />` 如果加载失败，默认渲染空节点，你很可能需要根据当前 UI 设置属于自己的错误反馈节点。

- `brokenElement` - 设置加载失败图案 `React.ReactElement`
- `loadingElement` - 设置加载中图案 `React.ReactElement`

<DocBroken />
